use std::str::FromStr;

/// 十六进制数的字符数组
const HEX_TABLE: [char;16] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'];

/// 将f32类型的浮点数格式化为符合LLVM标准的64位16进制表示
pub fn format_double(f: f32) -> String {
    let f = f as f64;
    let ptr = &f as *const f64;
    let mut int_num: u64;
    unsafe {
        int_num = *(ptr as *const u64);
    }
    let mut res = String::from("");
    for _ in 0..16 {
        let idx = int_num&0xF;
        res.push(HEX_TABLE[idx as usize]);
        int_num = int_num >> 4;
    }
    let mut res = res.chars().rev().collect::<String>();
    res.insert_str(0, "0x");
    res
}

/// 将f32类型的浮点数格式化为32位16进制表示
pub fn format_float(f: f32) -> String {
    let ptr = &f as *const f32;
    let mut int_num: u32;
    unsafe {
        int_num = *(ptr as *const u32);
    }
    let mut res = String::from("");
    for _ in 0..8 {
        let idx = int_num&0xF;
        res.push(HEX_TABLE[idx as usize]);
        int_num = int_num >> 4;
    }
    let mut res = res.chars().rev().collect::<String>();
    res.insert_str(0, "0X");
    res
}

pub fn double_to_float(f: &str) -> String {
    let float_num: f32;
    if f == "0.0" {
        float_num = 0.0;
    } else {
        float_num = parse_float(f);
    }
    format_float(float_num)
}

/// 解析十六进制小数
fn parse_hex_decimal(s: &str) -> f64 {
    s.chars().map(|x|{
        let res: u8;
        if x >= 'A' && x <= 'Z'{
            res = x as u8 - 'A' as u8 + 10;
        } else if x >= 'a' && x <= 'z'{
            res = x as u8 - 'a' as u8 + 10;
        } else if x >= '0' && x <= '9' {
            res = x as u8 - '0' as u8;
        } else {
            panic!("Wrong number {}, found {}", s, x);
        }
        res
    }).rev().fold(0_f64, |acc, x| {
        let y = x as f64;
        let res = acc/16.0 + y;
        res
    }) / 16.0
}

/// 解析十进制小数
fn parse_oct_decimal(s: &str) -> f64 {
    s.chars().map(|x|{
        if x >= '0' && x <= '9' {
            x as u8 - '0' as u8
        } else {
            panic!("Wrong number {}, found {}", s, x);
        }
    }).rev().fold(0_f64, |acc, x| {
        let y = x as f64;
        let res = acc/10.0 + y;
        res
    }) / 10.0
}

/// 解析纯浮点数，其以十六进制表示，反映的是浮点数的内部数据排列
fn parse_absolute_float(s: &str) -> f32 {
    if s.len() == 10 { // 2(0x)+8(8*4=32)
        let int = u32::from_str_radix(&s[2..], 16).expect(&format!("Wrong hex {}", s));
        let int_box = Box::new(int);
        let ptr = Box::<u32>::into_raw(int_box);
        let float_ptr = ptr as *mut f32;
        let float_num: Box<f32>;
        unsafe {
            float_num = Box::<f32>::from_raw(float_ptr);
        }
        *float_num
    } else if s.len() == 18 {
        let int = u64::from_str_radix(&s[2..], 16).unwrap();
        let int_box = Box::new(int);
        let ptr = Box::<u64>::into_raw(int_box);
        let float_ptr = ptr as *mut f64;
        let float_num: Box<f64>;
        unsafe {
            float_num = Box::<f64>::from_raw(float_ptr);
        }
        *float_num as f32
    } else {
        panic!("Length of hex {} wrong", s);
    }
}

/// 解析符合ISO标准的C语言浮点数
pub fn parse_float(s: &str) -> f32 {
    if s.contains("0x") || s.contains("0X") { // hex
        if s.contains("e") || s.contains("E") { // absolute hex (It's used to pass float between AST)
            parse_absolute_float(s)
        } else if s.contains("p") || s.contains("P") { // 0[xX][0-9a-zA-Z]*.[0-9a-zA-Z]p[+-][0-9]
            let v: Vec<&str>;
            if s.contains("p") {
                v = s.split("p").collect();
            } else {
                v = s.split("P").collect();
            }
            assert!(v.len() == 2, "float {} format is wrong", s);

            if v[0].contains(".") { // 0x1af.p2, 0xaf.fep-4
                let w: Vec<&str> = v[0].split(".").collect();
                let integer: f64;
                if &w[0][2..] != "" {
                    integer = u64::from_str_radix(&w[0][2..], 16).unwrap() as f64;
                } else {
                    integer = 0.0;
                }
                let decimal = parse_hex_decimal(w[1]); 
                
                let exp: f64 = f64::from_str(v[1]).unwrap();
                let res = (integer + decimal) * 2_f64.powf(exp);
                res as f32
            } else { // 0xap3
                let integer: f64;
                if &v[0][2..] != "" {
                    integer = u64::from_str_radix(&v[0][2..], 16).unwrap() as f64;
                } else {
                    integer = 0.0;
                }
                let exp: f64 = f64::from_str(v[1]).unwrap();
                let res = integer * 2_f64.powf(exp);
                res as f32
            }
        } else {
            if s.contains(".") { // 0x5e.ac
                let v: Vec<&str> = s.split(".").collect();
                let integer: f64;
                if &v[0][2..] != "" {
                    integer = u64::from_str_radix(&v[0][2..], 16).unwrap() as f64;
                } else {
                    integer = 0.0;
                }
                let integer = integer as f64;
                let decimal = parse_hex_decimal(v[1]); 

                (integer+decimal) as f32

            } else { // absolute hex
                parse_absolute_float(s)
            } // contains(".")
        } // contains("p")
    } else { // oct
        if s.contains("p") || s.contains("P") {
            panic!("Expect exponent, found binary exponent");
        } else if s.contains("e") || s.contains("E") {
            let v: Vec<&str>;
            if s.contains("e") {
                v = s.split("e").collect();
            } else {
                v = s.split("E").collect();
            }
            assert!(v.len() == 2, "float {} format is wrong", s);

            if v[0].contains(".") { // example: 1.324e-3, .635e+7, 122.e5
                let w: Vec<&str> = v[0].split(".").collect();
                let integer: f64;
                if w[0] != "" {
                    integer = u64::from_str_radix(&w[0], 10).unwrap() as f64;
                } else {
                    integer = 0.0;
                }
                let decimal = parse_oct_decimal(w[1]); 
                
                let exp: f64 = f64::from_str(v[1]).unwrap();
                let res = (integer + decimal) * 10_f64.powf(exp);
                res as f32
            } else { // example: 23e2
                let integer = u64::from_str_radix(&v[0], 10).unwrap() as f64;
                let exp: f64 = f64::from_str(v[1]).unwrap();
                let res = integer * 10_f64.powf(exp);
                res as f32
            }
        } else {
            if s.contains(".") {
                let v: Vec<&str> = s.split(".").collect();
                let integer: f64;
                if v[0] == "" {
                    integer = 0.0;
                } else {
                    integer = u64::from_str_radix(&v[0], 10).unwrap() as f64;
                }
                let decimal = parse_oct_decimal(v[1]); 

                (integer+decimal) as f32
            } else {
                f32::from_str(s).unwrap()
            } // contains(".")
        } // contains("e")
    } // contains("0x")
}

#[cfg(test)]
mod test_float {
    use super::*;
    use std::io::Write;
    #[test]
	fn conver_floats() {
	    const EXAMPLES: [&str; 12] = [
	        "5.5", 
	        "03.141592653589793", 
	        "1e-6", 
	        "0x1.921fb6p+1", 
	        "0x.AP-3", 
	        ".33E+5", 
	        "1e9",
	        "233",
	        "3.2",
	        "2.",
	        "1e1",
	        "0x1.7487b2p+7",
	    ];
        let mut output = std::fs::File::create("test_float.log").unwrap();
	    for example in EXAMPLES.iter() {
	        write!(output, "example = {}\n", example).unwrap();
	        let x = parse_float(example);
	        let hex_x = format_double(x);
	        write!(output, "x = {}, hex x = {}\n", x, hex_x).unwrap();
	
	        let new_x = parse_float(hex_x.as_str());
	        let new_hex_x = format_double(new_x);
	        write!(output, "new x = {}, new hex x = {}\n\n", new_x, new_hex_x).unwrap();
	    }
	}
}
